###########################################################################
# $Id: conceptDataManagerSQL.p 50 2009-07-15 08:54:51Z donbeave $
###########################################################################

@CLASS
conceptDataManagerSQL

@USE
concept/management/conceptDataManager.p
labour/tests/labourAssert.p

@BASE
conceptDataManager

@OPTIONS
locals

#----- Constructor -----

@create[hshOptions]
## hshOptions.sql
  ^BASE:create[$hshOptions]

  ^cleanMethodArgument[hshOptions]

  ^if(def $hshOptions.sql && $hshOptions.sql is pfSQL){
    $self._sql[$hshOptions.sql]
  }{
     $self._sql[]
   }

  $self.DATE_NOW[^date::now[]]

#----- Public -----

@addObject[objItem]
  ^labourAssert:required[objItem;conceptObjectItem]

  ^if($objItem.ID){
    ^pfAssert:fail[Object [ID: $objItem.ID] already in Data Base.]
  }

  $result(false)

  ^if(!^objItem.isExist[]){
    ^if(def $objItem._parent){
      ^pfAssert:isTrue(^objItem.parent.save[])[Can not save parent object.]
    }

#   заносим объект в БД
    ^_insertObject[$objItem]

#TODO добавить поддержку обновления threadID и sortOrder

#   получаем id занесенного объекта
    $iObjectID(^CSQL.lastInsertId[$STORAGE.tables.Objects])

#   adding texts
    ^objItem.texts.foreach[iLocale;oTextItem]{
      $oTextItem._ObjectID($iObjectID)

      $bIsAddedText(^addObjectText[$oTextItem])
    }

#   устанавливаем ID для объекта conceptObjectItem
    $objItem._ID($iObjectID)

#   меняем структуру временного хэша
    $STORAGE._objectsHash.[$iObjectID][$STORAGE._objectsHash.[$objItem.UID]]
    $STORAGE._objectsTextsHash.[$iObjectID][$STORAGE._objectsTextsHash.[$objItem.UID]]
#   очищаем временные данные
    ^STORAGE._objectsHash.delete[$objItem.UID]
    ^STORAGE._objectsTextsHash.delete[$objItem.UID]

    $result(true)
  }

@addObjectText[objTextItem]
  ^labourAssert:required[objTextItem;conceptObjectTextItem]

  $result(false)

  ^pfAssert:isTrue(^objTextItem.ObjectID.int(0))["ObjectID" not defined or wrong.]

  ^if(!^objTextItem.isExist[]){
    ^_insertObjectText[$objTextItem]

    $objTextItem._isExist(true)

    $result(true)
  }

@editObject[objItem]
  ^labourAssert:required[objItem;conceptObjectItem]

  $result(false)

  $tFields[$STORAGE.additionalObjectsFields]

  ^if(^objItem.isExist[]){
    ^CSQL.void{
      UPDATE
        `$STORAGE.tables.Objects`
      SET
        dt_update = ^CSQL.now[],
        sort_order = ^objItem.SortOrder.int(0),
        object_process_id = ^objItem.ProcessID.int(0),
        object_type_id = ^objItem.TypeID.int(0),
        parent_id = ^objItem.ParentID.int(0)
        ^if($tFields){
          ,
          ^tFields.menu{
            $sName[$tFields.field]

            $tFields.update =

            ^if($self.[_updateObject.$sName] is junction){
              ^self.[_updateObject.$sName][$objItem]
            }{
               ^if(def $objItem.$sName){
                 $oConverter[^STORAGE.getFieldConverter[Objects;$sName]]

                 ^if(def $oConverter){
                   '^oConverter.onUpdate[$objItem.$sName]'
                 }{
                    '$objItem.$sName'
                  }
               }{
                  NULL
                }
             }
          }[ , ]
        }
      WHERE
        object_id = ^objItem.ID.int(0)
    }

#   saving texts
    ^objItem.texts.foreach[iLocale;oTextItem]{
      $bIsAddedText(^oTextItem.save[])
    }

    $result(true)
  }

@editObjectText[objItem]
  ^labourAssert:required[objItem;conceptObjectTextItem]

  $result(false)

  $tFields[$STORAGE.additionalTextsFields]

  ^if(^objItem.isExist[]){
    ^CSQL.void{
      UPDATE
        `$STORAGE.tables.ObjectsTexts`
      SET
        name = '$objItem.Name',
        is_published = ^objItem.IsPublished.int(0)
        ^if($tFields){
          ,
          ^tFields.menu{
            $sName[$tFields.field]

            $tFields.update =

            ^if($self.[_updateObjectText.$sName] is junction){
              ^self.[_updateObjectText.$sName][$objItem]
            }{
               ^if(def $objItem.$sName){
                 $oConverter[^STORAGE.getFieldConverter[ObjectsTexts;$sName]]

                 ^if(def $oConverter){
                   '^oConverter.onUpdate[$objItem.$sName]'
                 }{
                    '$objItem.$sName'
                  }
               }{
                  NULL
                }
             }

          }[ , ]
        }
      WHERE
        object_id = ^objItem.ObjectID.int(0) AND
        locale_id = ^objItem.LocaleID.int(0)
    }

    $result(true)
  }

@removeObject[objItem]
  ^labourAssert:required[objItem;conceptObjectItem]

#TODO добавить проверку имеются ли у объекта потомки
#     возможно добавить force ключик, который бы отвечал за то как удалять данные
#     удалить ли сам объект со всеми его потомками, либо throw fuck off.....
  $result(false)

  ^if(^objItem.isExist[]){
    ^CSQL.void{
      DELETE
      FROM
        `$STORAGE.tables.Objects`
      WHERE
        object_id = ^objItem.ID.int(0)
    }

    $oTexts[$objItem.texts]

    ^oTexts.foreach[iLocaleID;oText]{
      $bIsTextRemoved(^removeObjectText[$oText])
    }

    ^if(^objItem.hasChildren[]){
      ^objItem.children.foreach[oChild]{
        $b(^oChild.remove[])
      }
    }

    $objItem._isExist(false)

    $result(true)
  }

@removeObjectText[objItem]
  ^labourAssert:required[objItem;conceptObjectTextItem]

  $result(false)

  ^if(^objItem.isExist[]){
    ^CSQL.void{
      DELETE
      FROM
        `$STORAGE.tables.ObjectsTexts`
      WHERE
        object_id = ^objItem.ObjectID.int(0) AND
        locale_id = ^objItem.LocaleID.int(0)
    }

    $result(true)
  }

@addBlock[objItem;intObjectID;intParentBlockID]
  ^if(^_insertBlock[$objItem]){
#   getting block's id
    $iBlockID(^CSQL.last_insert_id[$STORAGE.tables.Blocks])
  }{
    $iBlockID($objItem.ID)
  }

  ^if(^intObjectID.int(0)){
    ^_bindBlock[$iBlockID;^intObjectID.int(0);$intParentBlockID]
  }

@bindBlocks[strIDs;intObjectID;intParentBlockID]
  ^if(!(def $strIDs && ^intObjectID.int(0))){
    ^throw[Required params not specified or wrong]
  }

  $oBlocks[^STORAGE.getSharedBlocks[
    $.filter[
      $.IDs[$strIDs]
    ]
  ]]

  ^if($oBlocks){
    ^oBlocks.foreach[oBlock]{
      ^_bindBlock[$oBlock.ID;^intObjectID.int(0);^intParentBlockID.int(0)]
    }
  }

@editBlock[objItem]
  ^labourAssert:required[objItem;conceptBlockItem]

  $result(false)

  ^if(^objItem.ID.int(0)){
    ^CSQL.void{
      UPDATE
        `$STORAGE.tables.Blocks`
      SET
        data_type_id = ^objItem.DataTypeID.int(0),
        block_process_id = ^objItem.ProcessID.int(0),
        is_not_empty = ^if(def $objItem.Data){1}{0},
        is_published = ^objItem.IsPublished.int(1),
        is_shared = ^objItem.IsShared.int(0),
        is_parsed_manual = ^objItem.IsParsedManual.int(0),
        name = '$objItem.Name',
        description = ^if(def $objItem.Description){'$objItem.Description'}{NULL},
#TODO добавить конверторы данных (здесь нужен для параметров) преобразования их в текстовое представление.
        params = ^if(def $objItem.Params){NULL}{NULL},
        data = ^if(def $objItem.Data){'$objItem.Data'}{NULL}
      WHERE
        block_id = ^objItem.ID.int(0)
    }

    $result(true)
  }

@removeBlock[objItem;hshOptions]
## hshOptions.objectID
## hshOptions.parentBlockID
## hshOptions.force
  ^labourAssert:required[objItem;conceptBlockItem]

  ^cleanMethodArgument[hshOptions]

  $result(false)

  ^if(^objItem.ID.int(0)){
    ^if(($objItem.isShared && ^hshOptions.force.bool(false)) || !($objItem.isShared)){
      ^_deleteBlock[^objItem.ID.int(0);^hshOptions.objectID.int(0);^hshOptions.parentBlockID.int(0);^hshOptions.force.bool(false)]
    }{
      ^_unbindBlock[^objItem.ID.int(0);^hshOptions.objectID.int(0);^hshOptions.parentBlockID.int(0)]
    }
  }

#----- Private -----

@_insertObject[objItem]
  $tFields[$STORAGE.additionalObjectsFields]

  ^CSQL.void{
    INSERT INTO `$STORAGE.tables.Objects` (
#     statical fields
      dt_update,
      object_type_id,
      sort_order,
      parent_id,
      object_process_id

#     additional fields
      ^if($tFields){
        ,
        ^tFields.menu{
          $tFields.update
        }[,]
      }
    ) VALUES (
      ^CSQL.now[],
      $objItem.TypeID,
      $objItem.SortOrder,
      ^if(def $objItem._parent){$objItem._parent.ID}{$objItem.ParentID},
      ^if(^objItem.ProcessID.int(0)){^objItem.ProcessID.int(0)}{NULL}

      ^if($tFields){
        ^tFields.menu{
          $sName[$tFields.field]
          ,
          ^if($self.[_insertObject.$sName] is junction){
            ^self.[_insertObject.$sName][$objItem]
          }{
             ^if(def $objItem.$sName){
               $oConverter[^STORAGE.getFieldConverter[Objects;$sName]]

               ^if(def $oConverter){
                 '^oConverter.onUpdate[$objItem.$sName]'
               }{
                  '$objItem.$sName'
                }
             }{
                NULL
              }
           }
        }
      }
    )
  }

@_insertObjectText[objTextItem]
  $tFields[$STORAGE.additionalTextsFields]

  ^CSQL.void{
    INSERT INTO `$STORAGE.tables.ObjectsTexts` (
      locale_id,
      object_id,
      name,
      is_published

#     additional fields
      ^if($tFields){
        ,
        ^tFields.menu{
          $tFields.update
        }[,]
      }
    ) VALUES (
      ^if(^objTextItem.LocaleID.int(0)){^objTextItem.LocaleID.int(0)}{$STORAGE.defaultLocale.ID},
      ^objTextItem.ObjectID.int(0),
      '$objTextItem.Name',
      ^if(^objTextItem.IsPublished.bool(false)){1}{0}

      ^if($tFields){
        ^tFields.menu{
          $sName[$tFields.field]
          ,
          ^if($self.[_insertObjectText.$sName] is junction){
            ^self.[_insertObjectText.$sName][$objTextItem]
          }{
             ^if(def $objTextItem.$sName){
               $oConverter[^STORAGE.getFieldConverter[ObjectsTexts;$sName]]

               ^if(def $oConverter){
                 '^oConverter.onUpdate[$objTextItem.$sName]'
               }{
                  '$objTextItem.$sName'
                }
             }{
                NULL
              }
           }
        }
      }
    )
  }

@_insertBlock[objItem]
  ^labourAssert:required[objItem;conceptBlockItem]

  $result(false)

  ^if(!^objItem.ID.int(0)){
    ^CSQL.void{
      INSERT INTO `$STORAGE.tables.Blocks` (
        data_type_id,
        block_process_id,
        is_not_empty,
        is_published,
        is_shared,
        is_parsed_manual,
        name,
        params,
        description,
        data
      ) VALUES (
        ^objItem.DataTypeID.int(0),
        ^if(^objItem.ProcessID.int(0)){^objItem.ProcessID.int(0)}{NULL},
        ^if(def $objItem.Data){1}{0},
        ^objItem.IsPublished.int(1),
        ^objItem.IsShared.int(0),
        ^objItem.IsParsedManual.int(0),
        '$objItem.Name',
        ^if(def $objItem.Params){'$objItem.Params'}{NULL},
        ^if(def $objItem.Description){'$objItem.Description'}{NULL},
        ^if(def $objItem.Data){'$objItem.Data'}{NULL}
      )
    }

    $result(true)
  }

@_bindBlock[intBlockID;intObjectID;intParentBlockID]
  ^labourAssert:required[intBlockID;int]
  ^labourAssert:required[intObjectID;int]

    ^CSQL.void{
      INSERT INTO `$STORAGE.tables.Blocks_to_Objects` (
        block_id,
        ^if($CONCEPT.isTreeBlocksSupported){parent_block_id,}
        object_id,
        sort_order
      ) VALUES (
        ^intBlockID.int(0),
        ^if($CONCEPT.isTreeBlocksSupported){^intParentBlockID.int(0),}
        ^intObjectID.int(0),
        0
      )
    }

@_unbindBlock[intBlockID;intObjectID;intParentBlockID]
  ^labourAssert:required[intBlockID;int]
  ^labourAssert:required[intObjectID;int]

  $result(false)

    ^CSQL.void{
      DELETE
      FROM
        `$STORAGE.tables.Blocks_to_Objects`
      WHERE
        block_id = ^intBlockID.int(0) AND
        ^if($CONCEPT.isTreeBlocksSupported){parent_block_id = ^intParentBlockID.int(0) AND}
        object_id = ^intObjectID.int(0)
    }

@_deleteBlock[intBlockID;intObjectID;intParentBlockID;blnForce]
  $oChildrens[^STORAGE.getAllBlockChilds[$intBlockID;$intObjectID]]

# deleting dependencies to blocks
  ^CSQL.void{
    DELETE
    FROM
      `$STORAGE.tables.Blocks_to_Objects`
    WHERE
      object_id = ^intObjectID.int(0) AND
      (
        (^if($CONCEPT.isTreeBlocksSupported){parent_block_id = ^intParentBlockID.int(0) AND}
        block_id = ^intBlockID.int(0))

        ^if($oChildrens){
          OR (
            ^oChildrens.foreach[oChild]{
              (block_id = ^oChild.ID.int(0) AND
              parent_block_id = ^oChild.parentBlockID.int(0))
            }[ OR ]
          )
        }
      )
  }

#TODO добавить удаление связок у других объектов на shared блоки, если включен force.
#     чтобы не оставалось мусора

  ^CSQL.void{
    DELETE
    FROM
      `$STORAGE.tables.Blocks`
    WHERE
      block_id = ^intBlockID.int(0)
      ^if($oChildrens){
        OR (
          ^oChildrens.foreach[oChild]{
            ^if(($oChild.isShared && ^blnForce.bool(false)) || !$oChild.isShared){
              block_id = ^oChild.ID.int(0)
            }{
              block_id = NULL
            }
          }[ OR ]
        )
      }
  }

#----- Properties -----

@GET_CSQL[]
  ^if(!def $self._sql){
#TODO factory с использованием sqlOptions + connectString
#TODO проверка типа storage. он должен быть обязательно ConceptStorageSQL
      $self._sql[$STORAGE.CSQL]
  }
  $result[$_sql]
